package cn.edu.fzu.homemaking.sso.service.impl;

import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Maps;

import cn.edu.fzu.homemaking.common.CacheConst;
import cn.edu.fzu.homemaking.common.MD5Util;
import cn.edu.fzu.homemaking.common.SessionConstant;
import cn.edu.fzu.homemaking.exception.ServiceException;
import cn.edu.fzu.homemaking.redis.Redis;
import cn.edu.fzu.homemaking.sso.domain.entity.CompanyUser;
import cn.edu.fzu.homemaking.sso.domain.entity.Customer;
import cn.edu.fzu.homemaking.sso.domain.entity.SysUser;
import cn.edu.fzu.homemaking.sso.domain.entity.User;
import cn.edu.fzu.homemaking.sso.domain.enums.CompanyUserTypeEnum;
import cn.edu.fzu.homemaking.sso.domain.enums.SexEnum;
import cn.edu.fzu.homemaking.sso.domain.enums.StatusEnum;
import cn.edu.fzu.homemaking.sso.domain.vo.UserStatistic;
import cn.edu.fzu.homemaking.sso.mapper.UserMapper;
import cn.edu.fzu.homemaking.sso.service.UserService;

@Service("userService")
public class UserServiceImpl implements UserService {

    private final UserMapper                     userMapper;

    //<email,face>
    private Map<String, String>                  faceCache        = Maps.newConcurrentMap();

    private LoadingCache<String, Optional<User>> customerCache    = null;
    private LoadingCache<String, Optional<User>> companyUserCache = null;
    private LoadingCache<String, Optional<User>> sysUserCache     = null;

    private long                                 FROM             = 0;
    private static final int                     MAX_LIMIT        = 1000;

    @Autowired
    public UserServiceImpl(UserMapper userMapper) {
        this.userMapper = userMapper;
        CacheLoader<String, Optional<User>> customerLoader = new CacheLoader<String, Optional<User>>() {
            @Override
            public Optional<User> load(String email) {
                Customer user = userMapper.getCustomerByEmail(email);
                if (user == null) {
                    return Optional.empty();
                }
                return Optional.of(user);
            }
        };
        CacheLoader<String, Optional<User>> companyUserLoader = new CacheLoader<String, Optional<User>>() {
            @Override
            public Optional<User> load(String email) {
                CompanyUser user = userMapper.getCompanyUserByEmail(email);
                if (user == null) {
                    return Optional.empty();
                }
                return Optional.of(user);
            }
        };
        CacheLoader<String, Optional<User>> sysUserLoader = new CacheLoader<String, Optional<User>>() {
            @Override
            public Optional<User> load(String email) {
                SysUser user = userMapper.getSysUserByEmail(email);
                if (user == null) {
                    return Optional.empty();
                }
                return Optional.of(user);
            }
        };
        customerCache = CacheBuilder.newBuilder().initialCapacity(1000).expireAfterAccess(5, TimeUnit.MINUTES)
                .build(customerLoader);
        companyUserCache = CacheBuilder.newBuilder().initialCapacity(1000).expireAfterAccess(5, TimeUnit.MINUTES)
                .build(companyUserLoader);
        sysUserCache = CacheBuilder.newBuilder().initialCapacity(1000).expireAfterAccess(5, TimeUnit.MINUTES)
                .build(sysUserLoader);
    }


    @Scheduled(initialDelay = CacheConst.DEFAULT_INTERVAL, fixedDelay = CacheConst.DEFAULT_INTERVAL)
    @PostConstruct
    public void load() {
        Map<String, String> faceMap = new HashMap<>();
        List<User> users = null;
        do {
            users = userMapper.listAllFace(FROM, MAX_LIMIT);
            for (User user : users) {
                faceMap.put(user.getEmail(), user.getFaceUrl());
            }
            FROM += users.size();
        } while (users.size() == MAX_LIMIT);
        faceCache.putAll(faceMap);

        //为什么这样做？map缩容之后，里面的数组未缩容，那岂不是会造成空间越来越大？应该进行适当缩容来减少内存占用吧？
        Map<String, String> map = Maps.newConcurrentMap();
        map.putAll(faceCache);
        faceCache = map;
    }


    @Override
    public User getUserByEmail(String email, String userType) {
        if (StringUtils.isBlank(email)) {
            return null;
        }
        if (userType != null) {
            switch (userType) {
                case SessionConstant.USER:
                    return customerCache.getUnchecked(email).orElse(null);
                case SessionConstant.SYS_USER:
                    return sysUserCache.getUnchecked(email).orElse(null);
                case SessionConstant.COMPANY:
                    return companyUserCache.getUnchecked(email).orElse(null);
            }
        }
        return customerCache.getUnchecked(email)
                .orElse(sysUserCache.getUnchecked(email).orElse(companyUserCache.getUnchecked(email).orElse(null)));
    }


    @Override
    public Map<String, String> allFace() {
        return faceCache;
    }


    @Transactional
    @Override
    public void addSysUser(String email, String password, String name, SexEnum sex, Date birth, String phone,
                           String address, String faceUrl, User currentUser) {
        if (!(currentUser instanceof SysUser)) {
            throw new ServiceException("非法访问，仅系统人员可操作");
        }
        User user = getUserByEmail(email, SessionConstant.USER);
        if (user != null) {
            throw new ServiceException("邮箱已注册");
        }
        User build = User.build(email, MD5Util.MD5Encode(password), name, sex, birth, phone, address, faceUrl);
        userMapper.insertUser(build);
        userMapper.insertSysUser(build.getId());
        sysUserCache.refresh(email);
    }


    @Override
    public void updateSysUser(Long id, String email, String phone, String address, String faceUrl, StatusEnum status,
                              User currentUser) {
        if (!(currentUser instanceof SysUser)) {
            throw new ServiceException("非法访问，仅系统人员可操作");

        }
        SysUser sysUser = userMapper.getSysUserById(id);
        userMapper.updateUser(sysUser.getId(), email, null, phone, address, faceUrl, status);
        refreshFaceCache(sysUser.getEmail(), faceUrl, status);
        sysUserCache.refresh(sysUser.getEmail());
    }


    @Override
    public List<SysUser> listSysUser(String email, String name, SexEnum sex, String phone, StatusEnum status,
                                     User currentUser) {
        if (!(currentUser instanceof SysUser)) {
            throw new ServiceException("非法访问，仅系统人员可查看");
        }
        return userMapper.listSysUser(email, name, sex, phone, status);
    }


    @Transactional
    @Override
    public void addCustomer(String email, String password, String name, SexEnum sex, Date birth, String phone,
                            String address, String faceUrl) {
        User userByEmail = getUserByEmail(email, SessionConstant.USER);
        if (userByEmail != null) {
            throw new ServiceException("邮箱已被注册");
        }
        User user = User.build(email, password, name, sex, birth, phone, address, faceUrl);
        userMapper.insertUser(user);
        userMapper.insertCustomer(user.getId());
        //避免顾客先登录再注册再登录，造成缓存中存储了null，出现用户不存在的报错
        customerCache.refresh(email);
    }


    @Override
    public void updateCustomer(Long id, String email, String phone, String address, String faceUrl, StatusEnum status,
                               User currentUser) {

        if (!(currentUser instanceof SysUser)) {
            throw new ServiceException("非法访问，仅系统人员可操作");
        }
        Customer customer = userMapper.getCustomerById(id);
        if (customer == null) {
            throw new ServiceException("用户不存在");
        }
        userMapper.updateUser(customer.getId(), email, null, phone, address, faceUrl, status);
        refreshFaceCache(customer.getEmail(), faceUrl, status);
        customerCache.refresh(customer.getEmail());
    }


    @Override
    public List<Customer> listCustomer(String email, String name, SexEnum sex, String phone, StatusEnum status,
                                       User currentUser) {
        if (!(currentUser instanceof SysUser)) {
            throw new ServiceException("非法访问，仅系统人员可查看");
        }
        return userMapper.listCustomer(email, name, sex, phone, status);
    }


    @Transactional
    @Override
    public void addCompanyUser(String email, String password, String name, SexEnum sex, Date birth, String phone,
                               String address, String faceUrl, Long companyId, Long labelId, CompanyUserTypeEnum type,
                               BigDecimal salary) {
        Assert.isTrue(type != CompanyUserTypeEnum.ADMIN, "不能添加公司管理员，管理员只能存在一个，且只能通过注册成为管理员");
        User user = User.build(email, password, name, sex, birth, phone, address, faceUrl);
        userMapper.insertUser(user);
        userMapper.insertCompanyUser(user.getId(), companyId, labelId, type, salary);
        companyUserCache.refresh(email);
    }


    @Transactional
    @Override
    public void updateCompanyUser(Long id, String email, String phone, String address, String faceUrl,
                                  StatusEnum status, Long labelId, User currentUser) {

        CompanyUser user = userMapper.getCompanyUserById(id);
        if (user == null) {
            throw new ServiceException("用户不存在");
        }
        if (currentUser instanceof Customer) {
            throw new ServiceException("非法访问，禁止操作");
        }
        if (currentUser instanceof CompanyUser) {
            CompanyUser companyUser = (CompanyUser) currentUser;
            if (companyUser.getType() != CompanyUserTypeEnum.ADMIN
                    || !Objects.equals(user.getCompany().getId(), companyUser.getCompany().getId())) {
                throw new ServiceException("非法访问");
            }
        }
        userMapper.updateUser(user.getId(), email, null, phone, address, faceUrl, status);
        if (labelId != null && !Objects.equals(user.getLabelId(), labelId)) {
            userMapper.updateCompanyUser(user.getId(), labelId);
        }
        refreshFaceCache(user.getEmail(), faceUrl, status);
        companyUserCache.refresh(user.getEmail());
    }


    @Override
    public List<CompanyUser> listCompanyUser(String email, String name, SexEnum sex, String phone, StatusEnum status,
                                             Long companyId, CompanyUserTypeEnum type, Long labelId, User currentUser) {
        if (currentUser instanceof Customer) {
            throw new ServiceException("非法访问，禁止操作");
        }
        if (currentUser instanceof CompanyUser) {
            CompanyUser companyUser = (CompanyUser) currentUser;
            if (companyUser.getType() != CompanyUserTypeEnum.ADMIN) {
                throw new ServiceException("非法访问，仅家政公司管理员可操作");
            }
            if (companyId != null) {
                if (!Objects.equals(companyId, companyUser.getCompany().getId())) {
                    return Collections.emptyList();
                }
            } else {
                companyId = companyUser.getCompany().getId();
            }
        }
        return userMapper.listCompanyUser(email, name, sex, phone, status, companyId, type, labelId);
    }


    @Override
    public List<CompanyUser> listAllCompanyUser(CompanyUserTypeEnum type) {
        return userMapper.listCompanyUser(null, null, null, null, StatusEnum.NORMAL, null, type, null);
    }


    @Override
    public void updateUser(User user, String email, String password, String phone, String address, String faceUrl,
                           StatusEnum status) {
        userMapper.updateUser(user.getId(), email, password, phone, address, faceUrl, status);
        if (StringUtils.isBlank(email)) {
            return;
        }
        if (user instanceof Customer) {
            customerCache.refresh(email);
        } else if (user instanceof CompanyUser) {
            companyUserCache.refresh(email);
        } else if (user instanceof SysUser) {
            sysUserCache.refresh(email);
        }
    }


    @Redis(value = "NAME:%d_%s")
    @Override
    public String getNameById(Long id, String userType) {
        if (SessionConstant.COMPANY.equals(userType)) {
            return userMapper.getNameByCompanyUserId(id);
        } else if (SessionConstant.USER.equals(userType)) {
            return userMapper.getNameByCustomerId(id);
        }
        return null;
    }


    /** 刷新人脸 **/
    private void refreshFaceCache(String email, String faceUrl, StatusEnum status) {
        if (status == StatusEnum.NORMAL) {
            if (StringUtils.isNotBlank(faceUrl)) {
                faceCache.put(email, faceUrl);
            }
            return;
        }
        faceCache.remove(email);
    }


    @Override
    public UserStatistic countUser(User currenUser) {
        if (!(currenUser instanceof SysUser)) {
            throw new ServiceException("非法访问，仅系统人员可查看");
        }
        Long customerNum = userMapper.countCustomer();
        Long companyUserNum = userMapper.countCompanyUser();

        return new UserStatistic(customerNum, companyUserNum);
    }
}
